

#include <cstdlib>
#include <cassert>
#include <utility>

#include "Core\Input.h"

#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")


Input::Input()
	: m_direct_input(NULL),
	  m_mouse(NULL),
	  m_keyboard(NULL),
	  m_mouse_state(NULL),
	  m_initialized(FALSE)
{
}


Input::~Input()
{
}


BOOL Input::IsValid() const
{
	return m_initialized;
}


BOOL Input::Initialize(HINSTANCE hinstance, HWND hwnd)
{
	// Direct input initialization.
	HRESULT result;

	// Create direct input.
	result = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast< LPVOID* >(&m_direct_input), NULL);
	assert(SUCCEEDED(result));

	// Initialize mouse device.
	result = m_direct_input->CreateDevice(GUID_SysMouse, &m_mouse, NULL);
	assert(SUCCEEDED(result));

	result = m_mouse->SetDataFormat(&c_dfDIMouse);
	assert(SUCCEEDED(result));

	result = m_mouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	assert(SUCCEEDED(result));

	// Initialize keyboard device.
	result = m_direct_input->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL);
	assert(SUCCEEDED(result));

	result = m_keyboard->SetDataFormat(&c_dfDIKeyboard);
	assert(SUCCEEDED(result));

	result = m_keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
	assert(SUCCEEDED(result));

	// Allocate and initialize state structures.
	m_mouse_state = new DIMOUSESTATE;
	assert(m_mouse_state);

	m_keys = new BYTE[KEYBOARD_SIZE];
	assert(m_keys);

	ZeroMemory(m_mouse_state, sizeof(*m_mouse_state));
	ZeroMemory(m_keys, sizeof(*m_keys) * KEYBOARD_SIZE);

	// Flag as initialized.
	m_initialized = TRUE;

	return TRUE;
}


VOID Input::Terminate()
{
	// Terminate the keyboard.
	if(m_keyboard)
	{
		m_keyboard->Unacquire();
		m_keyboard->Release();
		m_keyboard = NULL;
	}

	// Terminate the mouse.
	if(m_mouse)
	{
		m_mouse->Unacquire();
		m_mouse->Release();
		m_mouse = NULL;
	}

	// Terminate direct input.
	if(m_direct_input)
	{
		m_direct_input->Release();
		m_direct_input = NULL;
	}

	// Free state structures.
	if(m_mouse_state)
	{
		delete m_mouse_state;
		m_mouse_state = NULL;
	}

	if(m_keys)
	{
		delete[] m_keys;
		m_keys = NULL;
	}

	// Flag as uninitialized.
	m_initialized = FALSE;
}


BOOL Input::ExitPressed() const
{
	return m_keys[DIK_ESCAPE] & 0x80;
}


BOOL Input::ForwardPressed() const
{
	return m_keys[DIK_W] & 0x80;
}


BOOL Input::BackwardPressed() const
{
	return m_keys[DIK_S] & 0x80;
}


BOOL Input::StrafeLeftPressed() const
{
	return m_keys[DIK_A] & 0x80;
}


BOOL Input::StrafeRightPressed() const
{
	return m_keys[DIK_D] & 0x80;
}


BOOL Input::CameraControlPressed() CONST
{
	return m_mouse_state->rgbButtons[0] & 0x80;
}


FLOAT Input::MouseX() CONST
{
	return static_cast< FLOAT >(m_mouse_state->lX);
}


FLOAT Input::MouseY() CONST
{
	return static_cast< FLOAT >(m_mouse_state->lY);
}


BOOL Input::HDRPressed() CONST
{
	return m_keys[DIK_H] & 0x80;
}


BOOL Input::RenderTypePressed() CONST
{
	return m_keys[DIK_R] & 0x80;
}

BOOL Input::NextRTPressed() CONST
{
	return m_keys[DIK_G] & 0x80;
}


BOOL Input::DisplayFPS() CONST
{
	return m_keys[DIK_F] & 0x80;
}


BOOL Input::NewLight() CONST
{
	return m_keys[DIK_Q] & 0x80;
}

BOOL Input::DeleteLight() CONST
{
	return m_keys[DIK_E] & 0x80;
}


BOOL Input::Update()
{
	BOOL result;

	// Read current mouse state.
	result = ReadMouse();

	if(!result)
	{
		return FALSE;
	}

	// Read current keyboard state.
	result = ReadKeyboard();

	if(!result)
	{
		return FALSE;
	}

	// TODO : Process raw device data to object oriented features.

	return TRUE;
}

BOOL Input::ReadMouse()
{
	// Read the new mouse state.
	HRESULT result = m_mouse->GetDeviceState(sizeof(*m_mouse_state), reinterpret_cast< LPVOID >(m_mouse_state));

	if(FAILED(result))
	{
		// Try to re-acquire the device.
		if(result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
		{
			result = m_mouse->Acquire();
			ZeroMemory(m_mouse_state, sizeof(DIMOUSESTATE));
		}
	}

	return TRUE;
}


BOOL Input::ReadKeyboard()
{
	// Read the new keyboard state.
	HRESULT result = m_keyboard->GetDeviceState(sizeof(*m_keys) * KEYBOARD_SIZE, reinterpret_cast< LPVOID >(m_keys));

	if(FAILED(result))
	{
		// Try to re-acquire the device.
		if(result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED)
		{
			result = m_keyboard->Acquire();
			ZeroMemory(m_keys, sizeof(BYTE) * KEYBOARD_SIZE);
		}
	}

	return TRUE;
}
